﻿Imports ARSoft.Tools.Net.Dns
Imports System.Net
Imports System.Management
Imports System.Net.NetworkInformation

Module modMain

    Private Const LOGIN_DNS_NAME As String = "login.minecraft.net"
    Private Const SESSION_DNS_NAME As String = "session.minecraft.net"
    Private DivertIP As New IPAddress(New Byte() {192, 168, 1, 73})

    Private WithEvents ThisDNSServer As DnsServer
    Private WithEvents GoogleDNSClient As DnsClient

    Sub Main()

        Console.WriteLine("MinecraftUnlocked-DNS Ver " & My.Application.Info.Version.ToString & " by GeorgeBush.")
        Console.WriteLine()

        ' Step 1: Start our own DNS Server
        Console.Write("Starting DNS Server... ")
        Try
            ThisDNSServer = New DnsServer(10, 10, AddressOf ThisDNSServer_ProcessQuery)
            ThisDNSServer.Start()
            Console.WriteLine("OK")
        Catch ex As Exception
            MsgBox(ex.Message)
        End Try

        ' Step 2: Start Google DNS Server Client to resolve non WoT related FQDN's
        Console.Write("Preparing Google DNS Client... ")
        Try
            GoogleDNSClient = New DnsClient(New IPAddress(New Byte() {8, 8, 8, 8}), 3600)
            Console.WriteLine("OK")
        Catch ex As Exception
            MsgBox(ex.Message)
        End Try

        ' Step 3: Load the IP address we should divert login.minecraft.net to.


        ' Step 4: Tell the pc to use our DNS Server
        Console.Write("Setting PC's DNS Server... ")
        If Not WMI_SetDNSServers(New String() {"127.0.0.1", "192.168.1.73"}) Then
            Console.WriteLine("FAILED")
            Console.WriteLine()
            Console.WriteLine("Press any key to exit...")
            Console.ReadKey()
            End
        End If
        Console.WriteLine("OK")

        Console.WriteLine()
        Console.WriteLine("Press any key to stop the DNS Server... ")
        Console.WriteLine()
        Console.ReadKey()

        ' Step 5: Revert DNS Settings
        Console.Write("Reverting PC's DNS Servers... ")
        If Not WMI_RestoreDNSServers() Then
            Console.WriteLine("FAILED")
        End If
        Console.WriteLine("OK")

        ThisDNSServer.Stop()

    End Sub

    Private DNSServers As Hashtable
    Private Function WMI_RestoreDNSServers() As Boolean
        Dim mClass As New ManagementClass("Win32_NetworkAdapterConfiguration")
        Dim mObjCol As ManagementObjectCollection = mClass.GetInstances()
        For Each mObj As ManagementObject In mObjCol
            If CType(mObj("IPEnabled"), Boolean) = True Then
                Dim Caption As String = CType(mObj("Caption"), String)
                If DNSServers.ContainsKey(Caption) Then
                    Dim mboDNS = mObj.GetMethodParameters("SetDNSServerSearchOrder")
                    If mboDNS IsNot Nothing Then
                        mboDNS("DNSServerSearchOrder") = DNSServers(Caption)
                        Dim mboReturn As ManagementBaseObject = mObj.InvokeMethod("SetDNSServerSearchOrder", mboDNS, Nothing)
                        If Not CType(mboReturn("returnValue"), UInt32) = 0 Then
                            Return False
                        End If
                    End If
                End If
            End If
        Next
        mObjCol.Dispose()
        mClass.Dispose()
        mObjCol = Nothing
        mClass = Nothing
        Return True
    End Function
    Private Function WMI_SetDNSServers(DNS_IPs() As String) As Boolean

        DNSServers = Nothing
        DNSServers = New Hashtable

        Dim mClass As New ManagementClass("Win32_NetworkAdapterConfiguration")
        Dim mObjCol As ManagementObjectCollection = mClass.GetInstances()
        For Each mObj As ManagementObject In mObjCol
            'Console.WriteLine("Adapter Enabled: " & CType(mObj("IPEnabled"), Boolean).ToString)
            If CType(mObj("IPEnabled"), Boolean) = True Then

                Dim Caption As String = CType(mObj("Caption"), String)
                DNSServers.Add(Caption, mObj("DNSServerSearchOrder"))

                Dim mboDNS = mObj.GetMethodParameters("SetDNSServerSearchOrder")
                If mboDNS IsNot Nothing Then
                    mboDNS("DNSServerSearchOrder") = DNS_IPs
                    Dim mboReturn As ManagementBaseObject = mObj.InvokeMethod("SetDNSServerSearchOrder", mboDNS, Nothing)
                    If Not CType(mboReturn("returnValue"), UInt32) = 0 Then
                        Return False
                    End If
                End If
            End If
        Next
        mObjCol.Dispose()
        mClass.Dispose()
        mObjCol = Nothing
        mClass = Nothing
        Return True
    End Function

    Private Function ThisDNSServer_ProcessQuery(query As ARSoft.Tools.Net.Dns.DnsMessageBase, clientAddress As System.Net.IPAddress, protocolType As System.Net.Sockets.ProtocolType) As ARSoft.Tools.Net.Dns.DnsMessageBase

        Console.WriteLine("DNS from: " & clientAddress.ToString)

        query.IsQuery = False

        Dim ThisMessage As DnsMessage = CType(query, DnsMessage)
        If ThisMessage IsNot Nothing AndAlso ThisMessage.Questions.Count = 1 Then

            Dim ThisQuestion As DnsQuestion = ThisMessage.Questions(0)

            ' Check if this requested DNS entry matches one in our local database
            If LCase(ThisQuestion.Name) = LCase(LOGIN_DNS_NAME) Or LCase(ThisQuestion.Name) = LCase(SESSION_DNS_NAME) Then
                Console.WriteLine(String.Format("   Resp: {0} = {1}", ThisQuestion.Name, DivertIP.ToString))
                ThisMessage.AnswerRecords.Add(New ARecord(ThisQuestion.Name, 3600, DivertIP))
                ThisMessage.ReturnCode = ReturnCode.NoError
                Return ThisMessage
            End If

            ' If we reach here, we need to get the google dns response for this question
            Console.WriteLine("    Asking google for: " & ThisQuestion.Name)
            Dim ThisAnswer As DnsMessage = GoogleDNSClient.Resolve(ThisQuestion.Name, ThisQuestion.RecordType, ThisQuestion.RecordClass)
            If ThisAnswer IsNot Nothing Then
                Console.WriteLine("    Google found: " & ThisQuestion.Name)
                ThisMessage.AnswerRecords.AddRange(ThisAnswer.AnswerRecords.AsEnumerable)
                ThisMessage.AnswerRecords.AddRange(ThisAnswer.AdditionalRecords.AsEnumerable)
                ThisMessage.ReturnCode = ReturnCode.NoError
                Return ThisMessage

                ' No record found
            Else
                Console.WriteLine("    Google could not find: " & ThisQuestion.Name)
                query.ReturnCode = ReturnCode.ServerFailure
                Return query
            End If

        End If

        query.ReturnCode = ReturnCode.ServerFailure
        Return query

    End Function

End Module
